﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace SFP.Animation2D
{
    public class Layer
    {
        private LayerManager layerManager;

        private string name;
        private double zDepth;
        private bool scrollable = true;
        private bool visible = true;

        private double xScroll;
        private double yScroll;

        private List<LayerObject> layerObjects = new List<LayerObject>();

        public Layer() : this("Default") {}

        public Layer(double zDepth) : this("Default", zDepth) { }

        public Layer(string name) : this(name, 0) {}

        public Layer(string name, double zDepth)
        {
            this.name = name;
            setZDepth(zDepth);
        }

        internal void setLayerManager(LayerManager layerManager)
        {
            this.layerManager = layerManager;
        }

        public void addLayerObject(LayerObject layerObject)
        {
            layerObjects.Add(layerObject);
        }

        public void removeLayerObject(LayerObject layerObject)
        {
            if (layerObjects.Contains(layerObject))
            {
                layerObjects.Remove(layerObject);
            }
        }

        public List<LayerObject> getLayerObjects()
        {
            List<LayerObject> layerObjects = new List<LayerObject>();
            foreach (LayerObject layerObject in this.layerObjects)
            {
                layerObjects.Add(layerObject);
            }
            return layerObjects;
        }

        public void clearLayer()
        {
            layerObjects.Clear();
        }

        public string getName()
        {
            return name;
        }

        public void setName(string name)
        {
            this.name = name;
        }

        public double getZDepth()
        {
            return zDepth;
        }

        public void setZDepth(double zDepth)
        {
            if (zDepth < 0) zDepth = 0;

            if (layerManager != null)
            {
                layerManager.changeZDepth(this, zDepth);
            }
            else
            {
                this.zDepth = zDepth;
            }
        }

        internal void setZDepthByManager(double zDepth)
        {
            this.zDepth = zDepth;
        }

        public bool isScrollable()
        {
            return scrollable;
        }

        public void setScrollable(bool scrollable)
        {
            if (layerManager != null && this.scrollable != scrollable)
            {
                if (scrollable)
                {
                    this.scrollable = scrollable;
                    scrollX(layerManager.getXScroll());
                    scrollY(layerManager.getYScroll());
                }
                else
                {
                    scrollX(-layerManager.getXScroll());
                    scrollY(-layerManager.getYScroll());
                    this.scrollable = scrollable;
                }
            }
        }

        public bool isVisible()
        {
            return visible;
        }

        public void setVisible(bool visible)
        {
            this.visible = visible;
        }

        public double getXScroll()
        {
            return xScroll;
        }

        internal void setXScroll(double xScroll)
        {
            scrollX(xScroll - this.xScroll);
        }

        public double getYScroll()
        {
            return yScroll;
        }

        internal void setYScroll(double yScroll)
        {
            scrollY(yScroll - this.yScroll);
        }

        internal void scrollX(double value) {
            if (!scrollable) return;

            value *= zDepthFactor();
            xScroll += value;

            foreach (LayerObject layerObject in layerObjects)
            {
                layerObject.X -= value;
            }
        }

        internal void scrollY(double value) {
            if (!scrollable) return;

            value *= zDepthFactor();
            yScroll += value;

            foreach (LayerObject layerObject in layerObjects)
            {
                layerObject.Y += value;
            }
        }

        private double zDepthFactor()
        {
            return Math.Pow(Math.E, -.5 * square(zDepth));
        }

        private double square(double value)
        {
            return value * value;
        }

        internal void draw(Graphics g) {
            if (!visible) return;

            foreach (LayerObject layerObject in layerObjects) {
                layerObject.draw(g);
            }
        }
    }
}
